//
//  OperationAsync.m
//  OperationQueueDemo
//
//  Created by 苏沫离 on 2018/8/9.
//  Copyright © 2018年 苏沫离. All rights reserved.
//

#import "OperationAsync.h"

@implementation OperationAsync
@synthesize finished = _finished;
@synthesize executing = _executing;
@synthesize completionBlock = _completionBlock;


- (instancetype)init
{
    self = [super init];
    
    if (self)
    {
        _finished = NO;
        _executing = NO;
    }
    
    return self;
}

/*
```-(void)start```的默认更新操作的执行状态并调用```-(void)main```方法。
```-(void)start```还执行一些检查，以确保操作能够实际运行。例如，如果操作被取消或已经完成，此方法只返回，而不调用```-(void)main```。(在OS X v10.5中，如果操作已经完成，该方法将抛出异常。)如果操作当前正在执行或尚未准备执行，此方法将抛出NSInvalidArgumentException异常。在OS X v10.5中，该方法捕获并忽略主方法自动抛出的任何异常。在macOS 10.6和更高版本中，异常可以在此方法之外传播。您不应该允许异常在主方法之外传播。
 
注意：如果操作仍然依赖于尚未完成的其他操作，则认为它还没有准备好执行。
 
如果要实现并发操作，则必须重写```-(void)start```并使用它来启动操作。重写```-(void)start```时不能调用```super```。除了为任务配置执行环境之外，此方法的实现还必须跟踪操作的状态并提供适当的状态转换。当操作执行并随后完成其工作时，它应该分别为```isExecuting```和```isFinished```属性生成KVO通知。

如果想手动执行操作，可以显式调用此方法；在操作队列中的操作对象上不需调用该方法。将操作对象添加到队列后，队列将会在合适的时机调用```-(void)start```。
 
 */
- (void)start
{
    //检查操作是否被取消,在取消操作时尽可能快地退出
    if (self.isCancelled){
        //重写start方法，当operation执行完成或者被取消的时候，必须重写这个finished属性以及生成KVO通知
        [self willChangeValueForKey:@"isFinished"];
        _finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        
        //重写start方法，当operation执行完成或者被取消的时候，根据需要调用completionBlock
        if (_completionBlock){
            self.completionBlock();
        }
        return;
    }
    
    //重写start方法，当operation改变了执行状态时，必须重写这个executing属性以及生成KVO通知。
    [self willChangeValueForKey:@"isExecuting"];
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    _executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
}


/*
```-(void)main```默认什么也不做。自定义```NSOperation```类需要重写此方法以执行所需的任务。在实现中，不要调用```super```。这个方法将自动在NSOperation提供的```@autoreleasepool```中执行，所以您不需要在实现中创建自己的```@autoreleasepool```。
 
如果实现一个并发操作，可以在自定义的```-(void)start```中执行任务，也可以在```-(void)main```中执行任务。
 */
- (void)main
{
    //该自动释放池可以防止相关线程发生内存泄漏
    @autoreleasepool{
        //使用  try-catch 语句防止出现超出这个线程范围的异常情况
        @try{
            //检查操作是否被取消,在取消操作时尽可能快地退出
            if (self.isCancelled == NO) {
                NSLog(@"耗时任务执行中 ------- %@",NSThread.currentThread);
                [NSThread sleepForTimeInterval:3];//模拟耗时任务
                [self willChangeValueForKey:@"isFinished"];
                [self willChangeValueForKey:@"isExecuting"];
                _finished = YES;
                _executing = YES;
                [self didChangeValueForKey:@"isExecuting"];
                [self didChangeValueForKey:@"isFinished"];
                
                //重写main方法，当operation执行完成或者被取消的时候，根据需要调用completionBlock
                if (_completionBlock){
                    self.completionBlock();
                }
            }
            
        }@catch (NSException *exception){}
    }
}

- (BOOL)isAsynchronous{
    return YES;
}

- (BOOL)isExecuting{
    return _executing;
}

- (BOOL)isFinished{
    return _finished;
}


/*
```-(void)cancel```
 
该方法不会将操作强制停止；它更新```NSOperation```的内部```cancelled```以反映状态的变化。如果操作已经完成执行，调用该方法无实际意义；可以调用该方法取消在操作队列中但尚未执行的操作。
 */

@end
